package tdraw

import (
	"fmt"
	"os"
)

type TikzDrawer struct {
	fout *os.File
	scale float64
	colors []string
	nnode int
}

type TikzAniDrawer struct {
	* TikzDrawer
	fprep string
	frame int
	bound [][]float64
}

func InitTikzDrawer (fout_name string, scale float64, bound [][]float64) * TikzDrawer {
	var td TikzDrawer
	td.fout, _ = os.Create (fout_name)
	head, _ := os.ReadFile("tikz_header.tex")
	td.fout.Write(head)
	fmt.Fprintf (td.fout, "\\begin{tikzpicture}[x = %fcm, y = %fcm, line width=0.5pt]\n", scale, scale)
	td.scale = scale

	fmt.Fprintf (td.fout, "\\clip[draw] (%f, %f) rectangle (%f, %f) ;\n", bound[0][0], bound[0][1], bound[1][0], bound[1][1])

	td.colors = []string {
		"black",
		"red",
		"green",
		"blue",
		"yellow",
		"olive",
		"cyan",
	}
	return &td
}

func (td * TikzDrawer) Finish () {
	foot, _ := os.ReadFile ("tikz_foot.tex")
	td.fout.Write(foot)
}

func (td * TikzDrawer) GetNumStyle () int {
	return len (td.colors)
}

func (td * TikzDrawer) MakeNode  (loc []float64, size float64, text []string, shape Shape, style int) (ind int) {

	if (len(text) == 0) {
		text = append (text, "")
	}

	var ss string
	switch shape {
	case Invisible:
		fmt.Fprintf (td.fout, "\\coordinate")
	case Rectangle:
		if len(text) <= 1 {
			fmt.Fprintf (td.fout, "\\node [shape=rectangle, minimum size=%fcm, draw=black, fill=%s!30]", size, td.colors[style])
		} else {
			fmt.Fprintf (td.fout, "\\node [shape=rectangle split, rectangle split parts=%d, minimum size=%fcm, draw=black, fill=%s!30]", len(text), size, td.colors[style])
		}
	case Circle:
		if len(text) >= 2 { ss = "split" }
		fmt.Fprintf (td.fout, "\\node [shape=circle %s, minimum size=%fcm, draw=black, fill=%s!30]", ss, size, td.colors[style])
	case TextBox:
		fmt.Fprintf (td.fout, "\\node")
	default:
		return -1
	}

	fmt.Fprintf (td.fout, " (n%d) at (%f, %f) ", td.nnode, loc[0], loc[1])

	if shape > 0 {
		if shape == TextBox {
			fmt.Fprintf (td.fout, "{\\color{%s}", td.colors[style])
		} else {
			fmt.Fprintf (td.fout, "{")
		}

		fmt.Fprintf (td.fout, "%s", text[0])
		if shape == Rectangle {
			nstr := []string {"one", "two", "three", "four"}
			for i := 1; i < 4; i ++ {
				if len(text) > i {
					fmt.Fprintf (td.fout, "\\nodepart{%s}%s", nstr[i], text[i])
				}
			}
		}
		if shape == Circle {
			if len(text) > 1 {
				fmt.Fprintf (td.fout, "\\nodepart{lower}%s", text[1])
			}
		}
		fmt.Fprintf (td.fout, "};\n")
	} else {
		fmt.Fprintf (td.fout, "{};\n")
	}

	ind = td.nnode
	td.nnode ++
	return 
}

func (td * TikzDrawer) DrawNodeLine  (i1, i2 int, arrow Arrow, text string, width float64, style int) {
	fmt.Fprintf (td.fout, "\\draw[color=%s,line width=%fcm", td.colors[style], width)

	switch arrow {
		case LeftArrow: 
		fmt.Fprintf (td.fout, ", -left to")
	case NormalArrow:
		fmt.Fprintf (td.fout, ", -angle 60")
	}

	fmt.Fprintf (td.fout, "] (n%d) -- ", i1)

	if len(text) != 0 {
		fmt.Fprintf (td.fout, "node[black, auto=left] {%s} ", text) 
	}

	fmt.Fprintf (td.fout, " (n%d) ;\n", i2)
}

func (td * TikzDrawer) DrawLine (p1, p2 []float64, arrow Arrow, text string, width float64, style int) {
	fmt.Fprintf (td.fout, "\\draw[color=%s,line width=%fcm", td.colors[style], width) 

	switch arrow {
		case LeftArrow: 
		fmt.Fprintf (td.fout, ", -left to")
	case NormalArrow:
		fmt.Fprintf (td.fout, ", -angle 60")
	}

	fmt.Fprintf (td.fout, "] (%f, %f) -- ", p1[0], p1[1]) 
	if len(text) != 0 {
		fmt.Fprintf (td.fout, "node[black, auto=left] {%s} ", text) 
	}
	fmt.Fprintf (td.fout, " (%f, %f) ;\n", p2[0], p2[1])
}

func (td * TikzDrawer) DrawPoint (loc []float64, size float64, style int) {
	fmt.Fprintf (td.fout, "\\draw[color=%s, fill] (%f, %f) circle (%fcm) ;\n", td.colors[style], loc[0], loc[1], size) 
}

func InitTikzAniDrawer (fprep string, scale float64, bound [][]float64) * TikzAniDrawer {
	var tad TikzAniDrawer
	fname := fprep + "_base.tex"
	tad.TikzDrawer = InitTikzDrawer (fname, scale, bound)
	tad.fprep = fprep
	tad.bound = bound
	return &tad
}

func (tad * TikzAniDrawer) StartAnimate () {
	tad.Finish() 
	fname := fmt.Sprintf ("%s_%04d.tex", tad.fprep, tad.frame)
	tad.TikzDrawer = InitTikzDrawer (fname, tad.scale, tad.bound)
}

func (tad * TikzAniDrawer) NextFrame () {
	tad.Finish() 
	tad.frame ++
	fname := fmt.Sprintf ("%s_%04d.tex", tad.fprep, tad.frame)
	tad.TikzDrawer = InitTikzDrawer (fname, tad.scale, tad.bound)
}

